package com.nanbei.lock;


import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.LockSupport;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @ClassDescription: wait和 notify 、 await 和 signal 、park 和 unpark 的用法
 * @JdkVersion: 1.8
 * @Author: libs
 * @Created: 2024/9/29 16:00
 */
public class LockSupportAPIDemo {

    public static final Object o = new Object();
    public static final ReentrantLock lock = new ReentrantLock();
    public static final Condition condition = lock.newCondition();

    public static void main(String[] args) {
        // wait和 notify
        // method1();

        // await 和 signal
        // method2();

        // park 和 unpark
        method3();


    }

    /**
     * LockSupport 提供park()和unpark()方法实现阻塞线程和解除线程阻塞的过程
     * LockSupport和每个使用它的线程都有一个许可(permit)关联。
     * 每个线程都有一个相关的permit, permit最多只有一个，重复调用unpark也不会积累凭证。
     */
    private static void method3() {
        Thread thread = new Thread(() -> {
            System.out.println("t1线程调用park()，阻塞当前线程");
            LockSupport.park();
            System.out.println("t1线程被唤醒，运行结束");
        }, "t1");
        thread.start();

        try { TimeUnit.MILLISECONDS.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); }

        new Thread(() -> {
            LockSupport.unpark(thread);
            System.out.println("t2线程唤醒t1线程");
        }, "t2").start();
    }

    /**
     * await() 和 signal() 必须在 lock.lock() 和 lock.unlock() 的保护下使用，否则会抛出 IllegalMonitorStateException。
     * await() 和 signal() 必须配合 ReentrantLock 使用，不能与 synchronized 关键字的锁一起使用。
     * 如果将 signal 放在 await 前面，线程无法被唤醒
     */
    private static void method2() {
        new Thread(() -> {
            lock.lock();
            try {
                System.out.println("t1线程进入等待状态,释放lock锁");
                condition.await();
                LockSupport.park();
                System.out.println("t1线程被唤醒，运行结束");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                lock.unlock();
            }
        }, "t1").start();

        try { TimeUnit.MILLISECONDS.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); }

        new Thread(() -> {
            lock.lock();
            condition.signal();
            System.out.println("t2线程唤醒t1线程");
            lock.unlock();
        }, "t2").start();
    }

    /**
     *  wait和notify方法必须要在同步块或者方法里面，且成对出现使用
     *  如果将 notify 放在 wait 前面，线程无法被唤醒
     */
    private static void method1() {
        Thread t1 = new Thread(() -> {
            synchronized (o) {
                try {
                    System.out.println("t1线程进入等待状态，释放锁");
                    o.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("t1线程被唤醒，运行结束");
            }
        }, "t1");
        t1.start();

        try { TimeUnit.MILLISECONDS.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); }

        new Thread(() -> {
            synchronized (o) {
                o.notify();
                System.out.println("t2线程唤醒t1线程");
            }
        }, "t2").start();
    }
}
